/**
 * 模块：通用请求工具
 * 说明：统一请求入口、响应格式与鉴权头注入（非登录接口）
 */
// =============== 配置 ===============
// API 基础配置 - 开发环境走代理，生产环境用真实地址
// 统一直连服务器地址（不使用开发代理）
export const API_BASE_URL = 'http://47.111.160.118:8080/'

// 通用响应类型
export interface ApiResponse<T = any> {
  code: string
  msg: string
  data: T
}

/**
 * 通用请求选项
 * - method: HTTP 方法，默认 'GET'
 * - headers: 额外请求头
 * - body: 请求体（仅非 GET 使用）
 * - params: 查询字符串参数
 * - skipDefaultHeaders: 跳过默认头（如保持简单请求避免预检）
 * - credentials: 携带 cookie 的模式（生产跨域常用 'include'）
 */
export interface RequestOptions {
  method?: 'GET' | 'POST' | 'PUT' | 'DELETE'
  headers?: Record<string, string>
  body?: any
  params?: Record<string, any>
  skipDefaultHeaders?: boolean
  credentials?: RequestCredentials
}

// =============== 工具函数 ===============
/**
 * 修复版大数 JSON 解析器 - 确保大数正确保存为字符串
 */
function parseJSONWithBigInt(text: string): any {
  if (!text.trim()) return {}
  
  try {
    // 更精确的正则匹配：匹配键值对中的大数
    // 匹配模式： "key": 12345678901234567
    const bigIntRegex = /"([^"]+)":\s*(\d{16,})/g
    
    const processedText = text.replace(bigIntRegex, (match, key, numberStr) => {
      // 检查是否为大数（超过安全整数范围）
      const num = Number(numberStr)
      if (num > Number.MAX_SAFE_INTEGER) {
        // 将大数转换为字符串形式
        return `"${key}": "${numberStr}"`
      }
      return match
    })
    
    return JSON.parse(processedText)
  } catch (error) {
    console.warn('大数处理解析失败，回退到普通解析:', error)
    // 回退到普通解析
    return JSON.parse(text)
  }
}

/**
 * 统一响应格式处理
 * @param raw 原始响应数据
 * @returns ApiResponse<T>
 */
export function normalizeResponse<T = any>(raw: any): ApiResponse<T> {
  const normalizedCode = raw?.code !== undefined ? String(raw.code) : (raw?.success === true ? '200' : '500')
  const normalizedMessage = raw?.message ?? raw?.msg ?? ''
  
  const normalizedData: T = normalizedCode === '200' && raw?.data === null 
    ? ({} as T) 
    : (raw?.data ?? raw?.result ?? ({} as T))
  
  return {
    code: normalizedCode,
    msg: normalizedMessage,
    data: normalizedData
  }
}

/**
 * 从登录接口返回的 Cookie 中读取最新 satoken
 * 说明：必须在发起登录请求时使用 credentials: 'include'，浏览器才会写入 Cookie。
 */
// 本地 token 存取（不再依赖 Cookie）
export function setToken(token: string): void {
  try {
    if (typeof window !== 'undefined' && window.localStorage) {
      // 统一保存多套兼容键，便于不同调用方读取
      window.localStorage.setItem('satoken', token)
      window.localStorage.setItem('Authorization', token)
      window.localStorage.setItem('token', token)
      window.localStorage.setItem('Token', token)
    }
  } catch (_) {
    // ignore localStorage access errors
  }
}

/**
 * 等待并获取登录后 Cookie 中的 satoken（轮询直至出现或超时）
 * @param timeoutMs 最长等待毫秒数（默认 1500ms）
 * @param intervalMs 轮询间隔毫秒数（默认 50ms）
 */
// 移除基于 Cookie 的等待逻辑，统一走本地存储

/**
 * 从本地获取 token（优先 localStorage，其次 Cookie）
 */
export function getToken(): string | null {
  try {
    if (typeof window !== 'undefined' && window.localStorage) {
      // 兼容多种本地保存方式，优先 satoken
      const keys = ['satoken', 'token', 'Token', 'TOKEN', 'authorization', 'Authorization']
      for (const k of keys) {
        const v = window.localStorage.getItem(k)
        if (v && v.trim()) return v.trim()
      }
    }
  } catch (_) {
    // ignore localStorage access errors
  }
  return null
}

/**
 * 判断是否为登录接口路径（跳过 token 注入）
 */
function isLoginEndpoint(path: string): boolean {
  const p = path.replace(/^\/+/, '')
  return /(^|\/)auth\/login$/.test(p) || /(^|\/)login$/.test(p)
}

// =============== 请求主函数 ===============
/**
 * 通用请求封装
 * @param url 请求地址（不要包含基础路径）
 * @param options 请求选项
 * @returns Promise<ApiResponse<T>>
 */
export async function request<T = any>(url: string, options: RequestOptions = {}): Promise<ApiResponse<T>> {
  try {
    const {
      method = 'GET',
      headers = {},
      body,
      params,
      skipDefaultHeaders = false,
      credentials = 'same-origin' // 默认使用 same-origin，避免 CORS 问题
    } = options

    // 清理 URL，移除可能的基础路径前缀
    let cleanUrl = url
      .replace(/^https?:\/\/[^\/]+/, '') 
      .replace(/^\/api\//, '') 
      .replace(/^\/+/, '') 
    
    // 始终使用固定基础地址
    let fullUrl = `${API_BASE_URL}${cleanUrl}` 
    
    // 处理查询参数
    if (params && Object.keys(params).length > 0) {
      const searchParams = new URLSearchParams()
      Object.entries(params).forEach(([key, value]) => {
        if (value !== undefined && value !== null) {
          searchParams.append(key, String(value))
        }
      })
      const paramString = searchParams.toString()
      fullUrl += paramString ? `?${paramString}` : ''
    }

    // 构建请求头
    const requestHeaders: Record<string, string> = skipDefaultHeaders ? {} : {
      'Content-Type': 'application/json',
      ...headers
    }

    // 非登录接口自动注入 Authorization: Bearer <token>
    const shouldSkipAuth = isLoginEndpoint(cleanUrl)
    const token = getToken()
    if (!shouldSkipAuth && token) {
      if (!requestHeaders['Authorization'] && !requestHeaders['authorization']) {
        requestHeaders['Authorization'] = `Bearer ${token}`
      }
      // 同时注入 Sa-Token 兼容头：satoken（若未手动设置）
      if (!requestHeaders['satoken']) {
        requestHeaders['satoken'] = token
      }
    }

    // 构建请求配置（移除环境分支，直接使用传入的 credentials）
    const config: RequestInit = {
      method,
      headers: requestHeaders,
      credentials: credentials
    }

    // 添加请求体
    if (body && method !== 'GET') {
      config.body = JSON.stringify(body)
    }

    // 发送请求
    const response = await fetch(fullUrl, config)

    // 尝试读取响应体（无论成功或失败）
    // 使用 text() 而不是 json() 来避免大整数精度丢失
    let raw: any
    try {
      const text = await response.text()
    // 从响应头中提取并保存 token（用于服务端通过 Header 返回 token 的场景）
    try {
      const authHeader = response.headers.get('Authorization') || response.headers.get('authorization')
      const saTokenHeader = response.headers.get('satoken') || response.headers.get('Sa-Token') || response.headers.get('SA-TOKEN')
      let extractedToken: string | null = null
      if (authHeader && /^Bearer\s+/i.test(authHeader)) {
        extractedToken = authHeader.replace(/^Bearer\s+/i, '').trim()
      } else if (saTokenHeader && saTokenHeader.trim()) {
        extractedToken = saTokenHeader.trim()
      }
      if (extractedToken) {
        setToken(extractedToken)
      }
    } catch (_) { /* ignore header/token extraction errors */ }

      // 使用修复版解析器保留大整数精度
      raw = parseJSONWithBigInt(text)
    } catch (e) {
      // 如果解析失败，抛出错误
      throw new Error(`HTTP error! status: ${response.status}, response: ${e instanceof Error ? e.message : String(e)}`)
    }

    if (!response.ok) {
      // 400 等错误时，返回的错误信息可能在响应体中
      const errorMsg = raw?.msg || raw?.message || raw?.error || raw?.data || `HTTP error! status: ${response.status}`
      console.error('请求失败:', {
        status: response.status,
        statusText: response.statusText,
        url: fullUrl,
        response: raw
      })
      throw new Error(errorMsg)
    }
    
    // 统一响应格式
    return normalizeResponse<T>(raw)
  } catch (error) {
    throw new Error('网络请求失败，请检查网络连接')
  }
}